# -*- coding: utf-8 -*-
# @version        : 1.0
# @Creaet Time    : 2021/10/12 22:27 
# @File           : case02.py
# @IDE            : PyCharm
# @desc           : 案例02，特殊请求操作


"""
特殊请求操作：
1. 定义 Cookie 参数
2. 定义 Header 参数
3. 定义 Request 参数
4. 接收表单数据
5. 单文件、多文件参数上传
"""

"""
使用Form接收表单数据时，需要执行安装：pip install python-multipart
"""

from fastapi import APIRouter, Cookie, Header, Request, Form, File, UploadFile
from typing import Optional, List

case02 = APIRouter()


@case02.get("/", summary="案例2初始接口")
def case02_root():
    return "case02_root"


@case02.get("/cookie/param", summary="定义Cookie参数")
def cookie_param(cookie_id: Optional[str] = Cookie(None)):
    """
    使用 Cookie 函数定义参数，可以直接获取到Cookie中的对应参数<br>
    例子：Cookie: cookie_id=xxxxxxxx
    如果需要直接获取到Cookie中的所有值，可以使用Header函数
    """
    return {"cookie_id": cookie_id}


@case02.get("/header/param", summary="定义Header参数")
def header_param(user_agent: Optional[str] = Header(None, convert_underscores=True), x_token: List[str] = Header(None)):
    """
    使用 Header 函数定义参数，可以直接获取到请求体中的对应参数<br>
    convert_underscores：如果请求体中为user-agent，则会将-转换为_匹配
    """
    return {"user_agent": user_agent, "x_token": x_token}


@case02.get("/request/param", summary="定义 Request 参数")
def request_param(request: Request, item: str):
    """
    使用 Request 类型参数，为请求体，可获取到所有请求内容<br>
    """
    print(item)
    print(request)
    print(request.url)
    print(request.client.host)
    print(request.headers)
    print(request.cookies)
    return {"url": request.url}

@case02.post("/reqeust/form", summary="接收表单数据")
def request_form(
        username: str = Form(..., title="用户名"),
        message: str = Form(..., title="留言内容")
):
    """
    接收表单数据，使用 Form 函数，其中基本属性与其他字段函数一致<br>
    请求时会发现'Content-Type: application/x-www-form-urlencoded' 不再为json格式

    """
    return {"username": username, "message": message}


@case02.post("/file", summary="File，上传单个文件")
def file(file: bytes = File(...)):
    """
    传递的参数类型为 bytes 字节数据类型，会以 bytes 形式写入内存<br>
    所以只能获取到数据内容，无法获取到文件信息，适合小文件上传<br>
    """
    return {"file_size": len(file)}


@case02.post("/files", summary="File，上传多个文件")
def files(files: List[bytes] = File(...)):
    return {"file_size": len(files)}


@case02.post("/upload/file", summary="UploadFile，上传单个文件")
async def upload_file(file: UploadFile = File(...)):
    """
    传递的参数类型为 UploadFile 类型，是直接可以通过Python操作的文件对象，能获取到文件信息<br>
    UploadFIle 具有以下 async 异步方法：<br>
    write(data)：写入data ( str 或 bytes ) 到文件<br>
    read(size)：读取文件的 size (int) 个字节/字符<br>
    seek(offset)：转到文件中的字节位置 offset(int)，如： await myfile.seek(0)  将转到文件的开头<br>
    close()：关闭文件<br>
    异步接口，所以需要在接口函数前加上 async<br>
    适合上传大文件
    """
    result = {
        "filename": file.filename,
        "content-type": file.content_type,
        "read": len(await file.read())
    }
    return result


@case02.post("/upload/files", summary="UploadFile，上传多个文件")
async def upload_files(files: List[UploadFile] = File(...)):
    for file in files:
        print(file.filename)
        print(file.content_type)
        contents = await file.read()
        print(contents)
    return "upload files"


@case02.post("/upload/files/save", summary="UploadFile，上传多个文件，并保存到本地")
async def upload_files_save(files: List[UploadFile] = File(...)):
    """
    首先先在项目根目录下创建一个static目录，用来存在静态资源，我们吧上传的文件都保存到这个目录中<br>
    然后我们使用异步方式读取上传过来的文件内容，读取后写入到本地文件中即可。

    异步方法原理：
    读取文件内容时是一个异步协程操作，执行流程：
    1. 先读取一个文件内容，这个属于IO操作，比较耗时，但是fastapi不会在这里等待读取完成
    2. 而是会再去获取到一个文件对象，继续执行读取操作
    """
    for file in files:
        try:
            data = await file.read()
            with open('static/' + file.filename, "wb") as f:
                f.write(data)
            return "success"
        except Exception as e:
            return str(e)
